package io.renren.common.utils.password;

import cn.hutool.core.util.ArrayUtil;
import io.renren.common.exception.RenException;
import org.springframework.util.StringUtils;

/**
 * 密码校验类
 *
 * @author txb
 * @since 2023-12-01 9:05
 */
public class PasswordCheckUtil {

    public static void checkPassword(String password) {
        if (!StringUtils.hasText(password)) {
            throw new RenException("密码不能为空");
        }
        //密码校验总开关
        if (PasswordConstant.PASS_WORD_CHECK_SWITCH) {
            //是否在自定义集合中
            if (PasswordConstant.CHECK_CUSTOM_PASSWORD_ARRAY) {
                checkCustomPasswordArray(password);
            }

            //检测长度
            if (PasswordConstant.CHECK_PASSWORD_LENGTH) {
                checkPasswordLength(password, PasswordConstant.LIMIT_PASSWORD_MAX_LENGTH, PasswordConstant.LIMIT_PASSWORD_MIN_LENGTH);
            }
            //检测包含数字
            if (PasswordConstant.CHECK_CONTAIN_DIGIT && !password.matches("(.*[0-9].*)")) {
                throwException();
            }
            //检测包含字母(区分大小写)
            if (PasswordConstant.CHECK_CONTAIN_UPPER_LOWER_CASE) {
                if (PasswordConstant.CHECK_CONTAIN_LOWER_CASE && !password.matches("(.*[a-z].*)")) {
                    throw new RenException("密码应由数字、大小写字母和特殊字符组成");
                }
                if (PasswordConstant.CHECK_CONTAIN_UPPER_CASE && !password.matches("(.*[A-Z].*)")) {
                    throw new RenException("密码应由数字、大小写字母和特殊字符组成");
                }
            } else {
                checkContainCase(password);
            }
            //检测包含特殊符号
            if (PasswordConstant.CHECK_CONTAIN_SPECIAL_CHAR && !checkContainSpecialChar(password)) {
                throwException();
            }
            //检测键盘横向连续
            if (PasswordConstant.CHECK_HORIZONTAL_KEY_SEQUENTIAL && checkLateralKeyboardSite(password)) {
                throw new RenException("密码不能包含键盘上连续输入的字符");
            }
            //检测键盘斜向连续
            if (PasswordConstant.CHECK_SLOPE_KEY_SEQUENTIAL && checkKeyboardSlantSite(password)) {
                throw new RenException("密码不能包含键盘上连续输入的字符");
            }

            //检测逻辑位置连续
            if (PasswordConstant.CHECK_LOGIC_SEQUENTIAL && checkSequentialChars(password)) {
                throw new RenException("密码不能包含连续字符,如123，abc");
            }

            //检测相邻字符是否相同
            if (PasswordConstant.CHECK_SEQUENTIAL_CHAR_SAME && checkSequentialSameChars(password)) {
                throw new RenException("密码不能包含相同字符");
            }

            //是否检测空格、换行、回车等特殊制表符
            if (PasswordConstant.CHECK_WHITE_SPACE && checkWhitespace(password)) {
                throw new RenException("密码不能包含空格、回车、换行等特殊制表字符");
            }
        }
    }

    public static void throwException() {
        if (PasswordConstant.CHECK_CONTAIN_UPPER_LOWER_CASE) {
            throw new RenException("密码应由数字、大小写字母和特殊字符组成");
        } else {
            throw new RenException("密码应由数字、字母和特殊字符组成");
        }
    }

    public static void checkCustomPasswordArray(String password) {
        for (String s : PasswordConstant.CUSTOM_PASSWORD_ARRAY) {
            if (password.equals(s)) {
                throw new RenException("密码不能为" + s);
            }
        }
    }

    public static void checkPasswordLength(String password, Integer max, int min) {
        if (password.length() < min) {
            throw new RenException("密码长度不能小于" + min + "位");
        }
        if (null != max && password.length() > max) {
            throw new RenException("密码长度不能大于" + max + "位");
        }
    }

    public static void checkContainCase(String password) {
        char[] charArray = password.toCharArray();
        int charCount = 0;
        for (char c : charArray) {
            if (Character.isLetter(c)) {
                charCount++;
            }
        }
        if (charCount == 0) {
            throw new RenException("密码应由数字、字母和特殊字符组成");
        }
    }

    public static boolean checkContainSpecialChar(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int specialCount = 0;
        for (char c : charArray) {
            if (PasswordConstant.DEFAULT_SPECIAL_CHAR.indexOf(c) != -1) {
                specialCount++;
            }
        }
        if (specialCount >= 1) {
            flag = true;
        }
        return flag;
    }

    public static boolean checkLateralKeyboardSite(String password) {
        //将所有输入字符转为小写
        String lowerCasePassword = password.toLowerCase();
        //键盘横向规则检测
        int arrLength = PasswordConstant.KEYBOARD_HORIZONTAL_ARR.length;
        int limitNumKey = PasswordConstant.LIMIT_HORIZONTAL_KEY_NUMBER;
        for (int i = 0; i + limitNumKey <= lowerCasePassword.length(); i++) {
            String str = lowerCasePassword.substring(i, i + limitNumKey);
            String distinguishStr = password.substring(i, i + limitNumKey);
            String revOrderStr;
            for (int j = 0; j < arrLength; j++) {
                String configStr = PasswordConstant.KEYBOARD_HORIZONTAL_ARR[j];
                revOrderStr = new StringBuffer(PasswordConstant.KEYBOARD_HORIZONTAL_ARR[j]).reverse().toString();
                //检测包含字母(区分大小写)
                if (PasswordConstant.CHECK_CONTAIN_UPPER_LOWER_CASE) {
                    //考虑 大写键盘匹配的情况
                    String upperStr = PasswordConstant.KEYBOARD_HORIZONTAL_ARR[j].toUpperCase();
                    if (configStr.contains(distinguishStr) || upperStr.contains(distinguishStr)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    String revUpperStr = new StringBuffer(upperStr).reverse().toString();
                    if (revOrderStr.contains(distinguishStr) || revUpperStr.contains(distinguishStr)) {
                        return true;
                    }
                } else {
                    if (configStr.contains(str)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    if (revOrderStr.contains(str)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static boolean checkKeyboardSlantSite(String password) {
        //将所有输入字符转为小写
        String lowerCasePassword = password.toLowerCase();
        //键盘斜线方向规则检测
        int arrLength = PasswordConstant.KEYBOARD_SLOPE_ARR.length;
        int limitNumKey = PasswordConstant.LIMIT_SLOPE_KEY_NUMBER;
        for (int i = 0; i + limitNumKey <= lowerCasePassword.length(); i++) {
            String str = lowerCasePassword.substring(i, i + limitNumKey);
            String distinguishStr = password.substring(i, i + limitNumKey);
            String revOrderStr;
            for (int j = 0; j < arrLength; j++) {
                String configStr = PasswordConstant.KEYBOARD_SLOPE_ARR[j];
                revOrderStr = new StringBuffer(PasswordConstant.KEYBOARD_SLOPE_ARR[j]).reverse().toString();
                //检测包含字母(区分大小写)
                if (PasswordConstant.CHECK_CONTAIN_UPPER_LOWER_CASE) {
                    //考虑 大写键盘匹配的情况
                    String upperStr = PasswordConstant.KEYBOARD_SLOPE_ARR[j].toUpperCase();
                    if (configStr.contains(distinguishStr) || upperStr.contains(distinguishStr)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    String revUpperStr = new StringBuffer(upperStr).reverse().toString();
                    if (revOrderStr.contains(distinguishStr) || revUpperStr.contains(distinguishStr)) {
                        return true;
                    }
                } else {
                    if (configStr.contains(str)) {
                        return true;
                    }
                    //考虑逆序输入情况下 连续输入
                    if (revOrderStr.contains(str)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static boolean checkSequentialChars(String password) {
        int limitNumber = PasswordConstant.LIMIT_LOGIC_NUMBER;
        int normalCount, reversedCount;
        //检测包含字母(区分大小写)
        if (!PasswordConstant.CHECK_CONTAIN_UPPER_LOWER_CASE) {
            password = password.toLowerCase();
        }
        char[] passwordArray = password.toCharArray();
        for (int i = 0; i + limitNumber <= password.length(); i++) {
            normalCount = 0;
            reversedCount = 0;
            for (int j = 0; j < limitNumber - 1; j++) {
                if (passwordArray[i + j + 1] - passwordArray[i + j] == 1) {
                    normalCount++;
                    if (normalCount == limitNumber - 1) {
                        return true;
                    }
                }
                if (passwordArray[i + j] - passwordArray[i + j + 1] == 1) {
                    reversedCount++;
                    if (reversedCount == limitNumber - 1) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static boolean checkSequentialSameChars(String password) {
        char[] passwordArray = password.toCharArray();
        int limitNumber = PasswordConstant.LIMIT_SEQUENTIAL_CHAR_NUMBER;
        int count;
        for (int i = 0; i + limitNumber <= password.length(); i++) {
            count = 0;
            for (int j = 0; j < limitNumber - 1; j++) {
                if (passwordArray[i + j] == passwordArray[i + j + 1]) {
                    count++;
                    if (count == limitNumber - 1) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static boolean checkWhitespace(String password) {
        boolean flag = false;
        char[] charArray = password.toCharArray();
        int specialCount = 0;
        for (char c : charArray) {
            if (ArrayUtil.contains(PasswordConstant.CHARS, c)) {
                specialCount++;
            }
        }
        if (specialCount >= 1) {
            flag = true;
        }
        return flag;
    }

    public static void main(String[] args) {
        checkPassword("1987@chx");
    }
}
